package org.dreamwork.cai.io.impl;

import org.dreamwork.cai.io.AbstractServiceProvider;
import org.dreamwork.concurrent.Looper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class SimpleProvider extends AbstractServiceProvider {
    private final Logger logger = LoggerFactory.getLogger (SimpleProvider.class);
    private final static String LOOP_NAME = "SimpleProvider";

    private boolean running = true;
    private int port;
    private ServerSocket server;

    @Override
    public void bind (int port) throws IOException {
        server = new ServerSocket (this.port = port);
        Looper.invokeLater (() -> {
            Looper.create (LOOP_NAME, 16);
            logger.info ("Simple Service Provider listen on {}", port);
            try {
                while (running) {
                    Socket socket = server.accept ();
                    InetSocketAddress addr = (InetSocketAddress) socket.getRemoteSocketAddress ();
                    if (addr.getAddress ().isLoopbackAddress () && addr.getPort () == CLOSING_PORT) {
                        logger.info ("received a closing require from localhost. closing the simple provider ...");

                        socket.close ();
                        break;
                    }

                    Looper.runInLoop (LOOP_NAME, new SimpleProviderWorker (socket, resolver));
                }

                logger.info ("simple provider work done.");
            } catch (IOException ex) {
                logger.warn (ex.getMessage (), ex);
            } finally {
                Looper.destory (LOOP_NAME);
                close (server);
            }
        });
    }

    @Override
    public void unbind () throws IOException {
        running = false;
        try (Socket socket = new Socket ("127.0.0.1", port, InetAddress.getByName ("127.0.0.1"), CLOSING_PORT)) {
            socket.getOutputStream ().write (0);
        }
    }

    private void close (Closeable closeable) {
        try {
            closeable.close ();
        } catch (IOException ex) {
            logger.warn (ex.getMessage (), ex);
        }
    }
}